home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d1 / freemacs.arc / BUFFERS.ASM next >
Assembly Source File  |  1988-03-17  |  25KB  |  1,185 lines

  1. ;History:1045,7,17
  2. test_device    equ    0
  3.     .xlist
  4.     include    memory.def
  5.  
  6.  
  7. bufseg    segment    public
  8.  
  9. next_buffer    dw    ?
  10.  
  11.     extrn    toptop: word
  12.     extrn    topbot: word
  13.     extrn    bottop: word
  14.     extrn    botbot: word
  15.  
  16.     extrn    memsize: word
  17.  
  18.     extrn    bufseg_size: word
  19.  
  20. bufseg    ends
  21.  
  22.  
  23. data    segment    byte public
  24.  
  25.     extrn    phd_seg: word
  26.  
  27.     public    textseg
  28. textseg        dw    ?
  29.  
  30. first_buffer    dw    ?        ;segment of first buffer
  31. last_para    dw    ?        ;segment after highest buffer
  32. limit_para    dw    ?        ;segment after highest available.
  33.  
  34. fcb_struc    struc
  35. fcb_drive    db    0
  36. fcb_fname    db    '        '
  37. fcb_ext        db    '   '
  38. fcb_curblk    dw    0
  39. fcb_recsz    dw    0
  40. fcb_filsz    dw    0,0
  41. fcb_date    dw    0
  42. fcb_time    dw    0
  43. fcb_res        dd    ?,?
  44. fcb_currec    db    0
  45. fcb_ranrec    dw    0,0
  46. fcb_struc    ends
  47.  
  48. device_header    struc
  49. device_next    dd    ?
  50. device_attr    dw    ?
  51. device_strategy    dw    ?
  52. device_intr    dw    ?
  53. device_name    db    '        '
  54. device_header    ends
  55.  
  56. ;static request header.
  57. srh_struc    struc
  58. srh_len        db    ?
  59. srh_unit    db    ?
  60. srh_cmd        db    ?
  61. srh_status    dw    ?
  62. srh_res        dd    ?,?
  63. srh_struc    ends
  64.  
  65.  
  66. ;static request header for reading and writing.
  67. srh_rw        struc
  68.         db    (size srh_struc) dup(?)
  69. crw_media    db    ?
  70. crw_taddr    dd    ?
  71. crw_cnt        dw    ?
  72. crw_start    dw    ?
  73. srh_rw        ends
  74.  
  75.  
  76. ;static request header for nondestructive read no wait.
  77. srh_ic        struc
  78.         db    (size srh_struc) dup(?)
  79. cic_char    db    ?
  80. srh_ic        ends
  81.  
  82. segofs    struc
  83. offs    dw    ?
  84. segm    dw    ?
  85. segofs    ends
  86.  
  87. nul_fcb    fcb_struc    <0,'NUL     ','   '>
  88.  
  89. our_device_name    db    '@',0
  90.  
  91. their_end    dd    ?
  92.  
  93. eof_mark    db    ']'
  94.  
  95.     extrn    inverse_flag: byte
  96.  
  97.     db    256 dup(?)
  98. mystack    label    byte
  99.  
  100. md_ctab    label    word
  101.     dw    md_unk            ; initialize
  102.     dw    md_unk            ; media check
  103.     dw    md_unk            ; build bpb
  104.     dw    md_unk            ; input control string
  105.     dw    md_in            ; input
  106.     dw    md_ichk            ; input check (buffer status)
  107.     dw    md_istat        ; input status
  108.     dw    md_succ            ; input flush
  109.     dw    md_out            ; output
  110.     dw    md_out            ; output with verify
  111.     dw    md_ostat        ; output status
  112.     dw    md_succ            ; output buffer flush
  113.     dw    md_unk            ; output control string
  114.  
  115. crlf_string    db    0dh,0ah
  116.  
  117. data    ends
  118.  
  119.  
  120. code    segment    byte public
  121. ;all the routines in this segment are entered with ds=data, es=data
  122.     assume    cs:code, ds:data, es:data, ss:data
  123.  
  124. ;the following externs are in 'memory'
  125.     extrn    read_mark: near
  126.     extrn    del_to_mark: near
  127.  
  128. ;the following externs are in 'redisplay'
  129.     extrn    redisplay: near
  130.     extrn    paint_screen: near
  131.  
  132.     extrn    init_entry: near
  133.     extrn    uninit_exit: near
  134.  
  135.     extrn    abort_fatal: near    ;fatal error handler
  136.  
  137.  
  138. our_sp        dw    ?
  139. our_ss        dw    ?
  140. ptrsav        dd    ?
  141. their_sp    dw    ?
  142. their_ss    dw    ?
  143.  
  144. our_device    device_header<-1,8000h, md_strat, md_intr, '@       '>
  145.  
  146. lf_flag        db    0        ;=1 if we should return LF next input.
  147.  
  148. parameters    dw    0
  149.         dw    80h, ?
  150.         dw    5ch, ?
  151.         dw    6ch, ?
  152.  
  153. copy_to_dioa:
  154. ;enter with si, cx->command.
  155. ;exit with cs:phd_dioa set to the string.
  156.     push    es
  157.     mov    es,phd_seg        ;copy the command to phd_dioa+1
  158.     mov    di,80h+1
  159.   if 0
  160.     mov    ax,3700h        ;get the switch char.
  161.     int    21h
  162.     mov    al,dl
  163.     stosb
  164.     mov    al,'C'            ;store -C or /C.
  165.     stosb
  166.   endif
  167.     rep    movsb
  168.     mov    al,CR            ;store the terminating CR.
  169.     stosb
  170.     sub    di,80h+1+1        ;don't count the CR.
  171.     mov    ax,di
  172.     mov    es:[80h],al        ;store the count.
  173.     pop    es            ;restore es.
  174.     ret
  175.  
  176.  
  177.     public    execute_program
  178. execute_program:
  179. ;enter with si, cx->path of filter to execute, di ->null terminated filename.
  180. ;exit with ax=return result.
  181.     push    di
  182.     call    copy_to_dioa
  183.     call    init_device
  184.     pop    di
  185.     call    actually_execute
  186.     push    ax
  187.     call    uninit_device
  188.     pop    ax
  189.     ret
  190.  
  191.  
  192.     public    execute_filter
  193. execute_filter:
  194. ;enter with:
  195. ;  si, cx->path of filter to execute,
  196. ;  al=mark to filter to.
  197. ;  di ->null terminated filename.
  198. ;exit with ax=return result.
  199.     mov    eof_mark,al
  200.  
  201.     push    di
  202.     call    copy_to_dioa
  203.     call    init_device
  204.     pop    di
  205.  
  206.     mov    bx,0            ;get a copy of stdin.
  207.     mov    ah,45h
  208.     int    21h
  209.     push    ax            ;remember the old handle.
  210.  
  211.     mov    bx,1            ;get a copy of stdout.
  212.     mov    ah,45h
  213.     int    21h
  214.     push    ax            ;remember the old handle.
  215.  
  216.     mov    dx,offset our_device_name
  217.     mov    ax,3d02h        ;open for read/write.
  218.     int    21h
  219.     mov    bx,ax            ;remember the handle.
  220.  
  221.     mov    cx,0            ;force stdin to the per device.
  222.     mov    ah,46h
  223.     int    21h
  224.  
  225.     mov    cx,1            ;force stdout to the per device.
  226.     mov    ah,46h
  227.     int    21h
  228.  
  229.     mov    ah,3eh            ;close the original handle.
  230.     int    21h
  231.  
  232.     mov    bx,0            ;set stdin to raw.
  233.     mov    ax,4400h        ;get the device info.
  234.     int    21h
  235.     mov    dh,0
  236.     or    dl,20h            ;set to raw mode.
  237.     mov    ax,4401h        ;set the device info.
  238.     int    21h
  239.  
  240.     call    actually_execute
  241.  
  242.     mov    dx,ax
  243.  
  244.     mov    bx,1            ;close stdout.
  245.     mov    ah,3eh
  246.     int    21h
  247.  
  248.     pop    bx            ;unredirect stdout.
  249.     mov    ah,46h
  250.     mov    cx,1
  251.     int    21h
  252.  
  253.     mov    bx,0            ;close stdin.
  254.     mov    ah,3eh
  255.     int    21h
  256.  
  257.     pop    bx            ;unredirect stdin.
  258.     mov    cx,0
  259.     mov    ah,46h
  260.     int    21h
  261.  
  262.     push    dx            ;save the result code.
  263.     call    uninit_device
  264.     pop    ax
  265.     ret
  266.  
  267.  
  268. actually_execute:
  269. ;enter with di -> filename, cs:phd_dioa = arguments.
  270.     push    di
  271.     call    uninit_exit
  272.     pop    di
  273.  
  274.     push    ds
  275.     push    es
  276.  
  277.     push    di
  278.     call    compact_buffers        ;make room for the program.
  279.     pop    di
  280.  
  281.     mov    ds,dx            ;get the para of the last buffer.
  282.     assume    ds:bufseg
  283.     mov    bx,dx            ;remember the last buffer here.
  284.  
  285.     call    buffer_paragraphs
  286.     add    bx,cx            ;compute the first free segment.
  287.  
  288.     mov    dx,phd_seg        ;subtract off the allocated segment.
  289.     sub    bx,dx
  290.     mov    es,dx            ;get es=allocated segment.
  291.     assume    es:nothing
  292.  
  293.     mov    ah,4ah            ;reduce ourself in size.
  294.     int    21h
  295.  
  296.     mov    our_sp,sp        ;remember our stack.
  297.     mov    our_ss,ss
  298.  
  299.     mov    ds,phd_seg        ;move ds first because we need ds.
  300.     assume    ds:nothing
  301.     mov    es,phd_seg
  302.     assume    es:nothing
  303.  
  304.     mov    dx,di            ;set up to execute the program.
  305.  
  306.     mov    ax,2901h        ;parse fcb1.
  307.     mov    si,81h            ;->phd_sdioa.
  308.     mov    di,5ch            ;->phd_fcb1
  309.     int    21h
  310.  
  311.     mov    ax,2901h        ;parse fcb2.
  312.     mov    di,6ch            ;->phd_fcb2.
  313.     int    21h
  314.  
  315.     push    ss            ;ss:dx -> filename to execute.
  316.     pop    ds
  317.     assume    ds:data
  318.     push    cs
  319.     pop    es
  320.     assume    es:code
  321.     mov    bx,offset parameters
  322.     mov    ax,phd_seg
  323.     mov    es:[bx]+4,ax        ;use original phd parameters.
  324.     mov    es:[bx]+8,ax
  325.     mov    es:[bx]+12,ax
  326.     mov    ax,4b00h
  327.     int    21h
  328.     jc    actually_execute_1
  329.     xor    ax,ax            ;make sure ax is zero if no errors.
  330. actually_execute_1:
  331.  
  332.     cli                ;get our stack back.
  333.     mov    ss,cs:our_ss
  334.     mov    sp,cs:our_sp
  335.     sti
  336.  
  337.     push    ax
  338.  
  339.     mov    bx,0ffffh        ;now grab all of memory again.
  340.     mov    es,phd_seg
  341.     mov    ah,4ah            ;see how much is available.
  342.     int    21h
  343.     mov    ah,4ah            ;grab all of it.
  344.     int    21h
  345.  
  346.     push    cs            ;reset the fatal error address.
  347.     pop    ds
  348.     mov    dx,offset abort_fatal
  349.     mov    ax,2524h
  350.     int    21h
  351.  
  352.     mov    ax,33h*256+1        ;turn break checking back off.
  353.     mov    dl,0            ;  in case someone turned it on.
  354.     int    21h
  355.  
  356.     pop    ax
  357.  
  358.     pop    es
  359.     pop    ds
  360.     assume    ds:data, es:data
  361.  
  362.     push    ax
  363.     call    init_entry
  364.     call    paint_screen
  365.     pop    ax
  366.     ret
  367.  
  368.  
  369. init_device:
  370.     push    es
  371.     mov    dx,offset nul_fcb
  372.     mov    ah,0fh            ;opfile
  373.     int    21h
  374.     les    di,dword ptr nul_fcb.fcb_res+1    ;get the device driver pointer for version 2.
  375.     mov    ah,30h            ;see if it's really version 2.
  376.     int    21h
  377.     cmp    al,2
  378.     je    init_device_1        ;it is, we have it.
  379.     les    di,dword ptr nul_fcb.fcb_res+2    ;get the device driver pointer for version 3.
  380. init_device_1:
  381.     mov    their_end.offs,di    ;remember where we were
  382.     mov    their_end.segm,es
  383.     les    di,dword ptr es:[di].device_next
  384.     cmp    di,-1            ;is this the end of the list?
  385.     jne    init_device_1        ;no - keep looking.
  386.     les    di,their_end        ;get the pointer to the end.
  387.     mov    es:[di].device_next.offs,offset our_device
  388.     mov    es:[di].device_next.segm,cs
  389.     pop    es
  390.  
  391.     ret
  392.  
  393.  
  394. uninit_device:
  395.     push    es
  396.     les    di,their_end        ;make es:di -> end of the device chain.
  397.     mov    ax,-1            ;make this the end again.
  398.     stosw
  399.     stosw
  400.     pop    es
  401.     ret
  402.  
  403.  
  404. ;    this is the dispatch table
  405.  
  406. ;    there are 2 entry points to a drive, the strategy entry point
  407. ;    and the interrupt entry point. when z-dos wishes to do an i/o
  408. ;    request, it first calls the strategy routine with a dword pointer
  409. ;    in the es:bx registers. the strategy routine simply saves this
  410. ;    pointer and returns to the system. the interrupt routine is then
  411. ;    called by z-dos. the interrupt routine retrieves the dword pointer
  412. ;    saved by the strategy routine. this pointer is the address of a
  413. ;    "request packet" that contains the information needed to carry
  414. ;    out the i/o request, such as transfer address, byte count, etc.
  415.  
  416. ;    the strategy routine
  417.  
  418. md_strat proc    far
  419.     mov    word ptr cs:ptrsav,bx
  420.     mov    word ptr cs:ptrsav+2,es        ; save es:bx as dword
  421.     ret
  422. md_strat endp
  423.  
  424. ;    now the interrupt routine.
  425.  
  426. md_intr    proc    far
  427.  
  428. ;    save everything
  429.  
  430.     mov    cs:their_ss,ss
  431.     mov    cs:their_sp,sp
  432.     cli
  433.     mov    ss,cs:our_ss
  434.     mov    sp,offset mystack
  435.     sti
  436.  
  437.     push    ax
  438.     push    bx
  439.     push    cx
  440.     push    dx
  441.     push    si
  442.     push    di
  443.     push    bp
  444.     push    es
  445.     push    ds
  446.  
  447.   if test_device
  448.     mov    si,word ptr cs:ptrsav        ; load ax:si with buffer addr
  449.     mov    ax,word ptr cs:ptrsav+2        ; load ax:si with buffer addr
  450.     mov    cx,(size srh_rw)        ;get count of bytes to transfer
  451.     mov    es,cs:our_ss
  452.     mov    ds,es:textseg
  453.     call    insert_string$
  454.   endif
  455.  
  456.     les    bx,cs:ptrsav            ; es:bx = request packet
  457.     mov    cl,es:[bx].srh_cmd        ; cl = requested command
  458.     xor    ch,ch
  459.     shl    cx,1                ; make into word offset
  460.     mov    si,cx                ; si = pointer to routine
  461.     jmp    ss:md_ctab[si]            ; go to it
  462.  
  463. ;*    md_in - character input
  464. ;
  465. ;    md_in reads the number of characters requested into the
  466. ;    address given in the request packet.
  467. ;
  468.  
  469. md_in:
  470.     mov    cx,es:[bx].crw_cnt    ;get the transfer count.
  471.     les    di,es:[bx].crw_taddr
  472.     jcxz    md_in_2
  473. md_in_1:
  474.     push    es
  475.     push    di
  476.     push    cx
  477.  
  478.     cmp    cs:lf_flag,0        ;do we have a LF to return?
  479.     je    get_char_1        ;no.
  480.  
  481.     mov    cs:lf_flag,0        ;clear the flag, return LF.
  482.     mov    al,LF
  483.     jmp    short get_char_3
  484. get_char_1:
  485.     mov    es,cs:our_ss        ;prepare to call read_mark.
  486.     mov    ds,cs:our_ss
  487.  
  488.     push    ds
  489.     mov    al,eof_mark        ;read the next char.
  490.     call    read_mark
  491.     jc    get_char_5        ;go if the mark is after the point.
  492.     xor    cx,cx            ;mark is before point - eof now.
  493. get_char_5:
  494.     lodsw                ;this might not be a valid char.
  495.     pop    ds
  496.  
  497.     jcxz    get_char_4        ;if no chars left, don't delete any!
  498.  
  499.     push    ax            ;now delete this char.
  500.     push    cx
  501.     mov    al,'>'
  502.     call    del_to_mark
  503.     pop    cx
  504.     pop    ax
  505.  
  506. get_char_4:
  507.  
  508.     stc
  509.     jcxz    get_char_2        ;no more chars - return cy.
  510.     cmp    cx,2            ;do we have less than two chars?
  511.     jb    get_char_3        ;yes - must be a single char.
  512.     cmp    ax,CR + LF*256        ;do we have a newline?
  513.     jne    get_char_3        ;no - must be a single char.
  514.     mov    al,CR            ;newline - return CR.
  515.     inc    cs:lf_flag        ;next time return a LF.
  516. get_char_3:
  517.     clc
  518. get_char_2:
  519.     pop    cx
  520.     pop    di
  521.     pop    es
  522.  
  523.     jc    md_in_2            ;eof before count satisfied.
  524.     stosb
  525.     loop    md_in_1
  526. md_in_2:
  527.     les    bx,cs:ptrsav        ; es:bx = packet
  528.     sub    es:[bx].crw_cnt,cx    ;subtract off the bytes not read.
  529.  
  530.     jmp    md_succ
  531.  
  532.  
  533. ;*    md_ichk - non-destructive input check
  534. ;
  535. ;    md_ichk returns the status of the input queue. a
  536. ;    return of busy indicates queue empty, otherwise
  537. ;    return character at front of queue (without removing
  538. ;    it).
  539. ;
  540.  
  541. md_ichk:
  542.     mov    es,cs:our_ss        ;prepare to call read_mark.
  543.     mov    ds,cs:our_ss
  544.  
  545.     mov    al,eof_mark        ;read the next char.
  546.     call    read_mark
  547.     lodsb                ;this might not be a valid char.
  548.  
  549.     les    bx,cs:ptrsav            ; es:bx = packet
  550.     mov    es:[bx].cic_char,al
  551.  
  552.     jcxz    md_ichk_1        ;no more chars - return busy.
  553.     jmp    md_succ
  554. md_ichk_1:
  555.     mov    ax,300h            ;return busy.
  556.     jmp    md_exit
  557.  
  558.  
  559. ;*    md_istat - input status
  560. ;
  561. ;    md_istat sets the busy bit if there is a character
  562. ;    ready to be read. if busy is set, the i/o will have
  563. ;    to go to the physical device. if busy is clear, the
  564. ;    read will return quickly.
  565. ;
  566.  
  567. md_istat:
  568.     jmp    md_succ            ; show always ready
  569.  
  570.  
  571. ;*    md_ifl - input queue flush
  572. ;
  573. ;    flush the input queue
  574. ;
  575.  
  576. md_ifl:
  577.     jmp    md_succ            ; consider it done!
  578.  
  579.  
  580. ;*    md_out - device output
  581. ;
  582. ;    md_out sends the output to the device
  583. ;
  584.  
  585. md_out:
  586.     mov    si,es:[bx].crw_taddr.offs    ; load ax:si with buffer addr
  587.     mov    ax,es:[bx].crw_taddr.segm    ; load ax:si with buffer addr
  588.     mov    cx,es:[bx].crw_cnt        ;get count of bytes to transfer
  589.     mov    es,cs:our_ss
  590.     mov    ds,es:textseg
  591.     call    insert_string$
  592.     jmp    md_succ            ;show success.
  593.  
  594.  
  595. ;*    md_ostat - output status
  596. ;
  597. ;    md_ostat returns the status of the device. if busy
  598. ;    is a 1, output will have to wait for device ready. if
  599. ;    busy is a 0, output will go directly to device with
  600. ;    no waiting.
  601. ;
  602.  
  603. md_ostat:
  604.     jmp    md_succ            ; output device is ready
  605.  
  606.  
  607. ;    the routines exit through md_succ if no error, or md_fail if
  608. ;    an error has occured
  609.  
  610. md_succ:
  611.     mov    ax,100h                ; show 'done' with no errors.
  612.     jmp    short md_exit
  613.  
  614. md_unk:
  615.     mov    al,3                ; unknown command
  616. md_fail:
  617.     mov    ah,80h                ; add the error bits
  618. md_exit:
  619.     les    bx,cs:ptrsav            ; es:bx = packet
  620.     mov    es:srh_status[bx],ax        ; save the status
  621.  
  622.   if test_device
  623.     mov    si,word ptr cs:ptrsav        ; load ax:si with buffer addr
  624.     mov    ax,word ptr cs:ptrsav+2        ; load ax:si with buffer addr
  625.     mov    cx,(size srh_rw)        ;get count of bytes to transfer
  626.     mov    es,cs:our_ss
  627.     mov    ds,es:textseg
  628.     call    insert_string$
  629.  
  630.     les    bx,cs:ptrsav
  631.     mov    si,es:[bx].crw_taddr.offs    ; load ax:si with buffer addr
  632.     mov    ax,es:[bx].crw_taddr.segm    ; load ax:si with buffer addr
  633.     mov    cx,es:[bx].crw_cnt        ;get count of bytes to transfer
  634.     jcxz    md_exit_1
  635.     mov    es,cs:our_ss
  636.     mov    ds,es:textseg
  637.     call    insert_string$
  638. md_exit_1:
  639.  
  640.     mov    si,offset crlf_string        ; load ax:si with buffer addr
  641.     mov    ax,ss                ; load ax:si with buffer addr
  642.     mov    cx,2                ;get count of bytes to transfer
  643.     mov    es,cs:our_ss
  644.     mov    ds,es:textseg
  645.     call    insert_string$
  646.  
  647.   endif
  648.  
  649.     pop    ds
  650.     pop    es
  651.     pop    bp
  652.     pop    di
  653.     pop    si
  654.     pop    dx
  655.     pop    cx
  656.     pop    bx
  657.     pop    ax
  658.     cli
  659.     mov    ss,cs:their_ss
  660.     mov    sp,cs:their_sp
  661.     sti
  662.     ret
  663.  
  664. md_intr    endp
  665.  
  666.  
  667.     public    get_next_buffer
  668. get_next_buffer:
  669.     push    ds
  670.     mov    ds,textseg
  671.     assume    ds:bufseg
  672.     mov    ax,next_buffer
  673.     pop    ds
  674.     assume    ds:data
  675.     ret
  676.  
  677.  
  678.     public    init_all_buffers
  679. init_all_buffers:
  680. ;enter with ax=>first paragraph of available memory, bx=> first paragraph of
  681. ;  unavailable memory.
  682. ;exit with cy if no buffer available.
  683.     mov    first_buffer,ax
  684.     mov    last_para,bx
  685.     mov    textseg,ax
  686.     mov    dx,ax
  687.     call    init_buffer
  688.     ret
  689.  
  690.     public    buffer_allocate
  691. buffer_allocate:
  692. ;entry:
  693. ;  case cx of
  694. ;    -1..-32768: report the current buffer number.
  695. ;      exit: ax=current buffer number.
  696. ;    0: create a new buffer.
  697. ;      exit: ax=new buffer number if enough memory, ax=0 otherwise.
  698. ;    1..32767:
  699. ;      entry: cx=buffer number to select, ax=0 for read/write buffer.
  700. ;      exit: ax=buffer number if it exists, ax=0 otherwise.
  701.     jcxz    buffer_allocate_2
  702.     or    cx,cx            ;if cx<0, return buffer number.
  703.     js    buffer_allocate_4_j
  704.     push    cx
  705.     push    ax
  706.     call    find_buffer
  707.     jc    buffer_allocate_5    ;buffer not found.
  708.  
  709.     push    ds
  710.     push    dx
  711.     mov    ds,dx            ;get the current buffer back.
  712.     call    select_buffer
  713.     pop    dx
  714.     pop    ds
  715.  
  716.     pop    cx            ;pushed as ax =0 if read/write buffer.
  717.     pop    ax            ;pushed as cx (buffer number).
  718.     or    cx,cx            ;read only buffer?
  719.     jne    buffer_allocate_4    ;yes - we're done.
  720.     push    ax
  721.     call    close_up_buffers
  722.     pop    cx            ;pushed as ax
  723.     mov    dx,first_buffer        ;find the buffer again.
  724.     assume    ds:bufseg
  725. buffer_allocate_6:
  726.     mov    ds,dx
  727.     mov    dx,next_buffer
  728.     loop    buffer_allocate_6
  729.     mov    dx,ds            ;get the current buffer back.
  730.     push    es
  731.     pop    ds
  732.     assume    ds:data
  733.     call    open_up_buffers
  734. buffer_allocate_4_j:
  735.     jmp    short buffer_allocate_4
  736. buffer_allocate_5:
  737.     add    sp,4            ;conserve the stack.
  738.     mov    ax,0            ;buffer not found.
  739.     jmp    short buffer_allocate_1
  740. buffer_allocate_2:
  741.     call    close_up_buffers    ;close all the buffers up.
  742.     mov    ds,dx            ;get the para of the last buffer.
  743.     assume    ds:bufseg
  744.     call    buffer_paragraphs    ;compute the size of it.
  745.     add    dx,cx            ;find the end of it.
  746.     call    init_buffer
  747.     mov    ax,0
  748.     jc    buffer_allocate_1    ;not enough memory.
  749.     mov    next_buffer,dx        ;point previous buffer to this one.
  750. buffer_allocate_4:
  751.     mov    bx,textseg
  752.     call    buffer_number        ;return number in ax.
  753. buffer_allocate_1:
  754.     push    es
  755.     pop    ds
  756.     assume    ds:data
  757.     ret
  758.  
  759.  
  760.     public    buffer_insert
  761. buffer_insert:
  762. ;enter with al=mark, cx=buffer number.
  763. ;insert the text between point and mark from the given buffer.
  764. ;exit with nc if ok, cy if the given buffer doesn't exist, or the specified
  765. ;  text won't fit.
  766.     push    textseg
  767.     push    ax
  768.     call    find_buffer        ;find their buffer.
  769.     assume    ds:bufseg
  770.     jc    buffer_insert_1        ;not found.
  771.  
  772.     push    ds
  773.     push    dx
  774.     mov    ds,dx            ;get the current buffer back.
  775.     call    select_buffer
  776.     pop    dx
  777.     pop    ds
  778.  
  779.     pop    ax
  780.  
  781.     push    dx
  782.     call    read_mark
  783.     pop    dx
  784.  
  785.     pop    ds            ;make ds->our buffer.
  786.     push    dx            ;save ->their buffer.
  787.     push    cx
  788.     call    select_buffer
  789.     pop    cx
  790.     pop    ax            ;pushed as dx.
  791.     call    insert_string$
  792.     jmp    short buffer_insert_2
  793. buffer_insert_1:
  794.     add    sp,4            ;get rid of mark, textseg.
  795.     stc
  796. buffer_insert_2:
  797.     push    es            ;restore ds.
  798.     pop    ds
  799.     assume    ds:data
  800.     ret
  801.  
  802.  
  803.     public    buffer_number
  804. buffer_number:
  805. ;enter with bx=paragraph of buffer.
  806. ;exit with ax=number of buffer.
  807.     push    ds
  808.     assume    ds:bufseg
  809.     mov    dx,first_buffer
  810.     xor    ax,ax
  811. buffer_number_1:
  812.     inc    ax
  813.     mov    ds,dx
  814.     cmp    dx,bx            ;is this the one we're looking for.
  815.     mov    dx,next_buffer        ;in any case, get the next buffer
  816.     jne    buffer_number_1
  817. buffer_number_2:
  818.     pop    ds
  819.     assume    ds:data
  820.     ret
  821.  
  822.  
  823.     public    succ_buffer
  824. succ_buffer:
  825. ;enter with bx=buffer number.
  826. ;exit with nc, bx=next nonempty buffer, dx=segment of next buffer,
  827. ;  cy if no other buffers or all other buffers are empty.
  828.     push    ds
  829.     mov    cx,bx
  830.     mov    ax,bx
  831.     mov    dx,first_buffer
  832.     assume    ds:bufseg
  833. succ_buffer_2:
  834.     cmp    dx,last_para        ;at the end?
  835.     stc
  836.     je    succ_buffer_3        ;yes - don't change the buffer number.
  837.     mov    ds,dx
  838.     mov    dx,next_buffer
  839.     loop    succ_buffer_2
  840. succ_buffer_0:
  841.     cmp    dx,last_para        ;are we at the end?
  842.     jne    succ_buffer_1
  843.     mov    ds,first_buffer        ;yes - start from the beginning again.
  844.     mov    dx,next_buffer        ;skip the first buffer.
  845.     mov    ax,1
  846. succ_buffer_1:
  847.     inc    ax            ;preincrement.
  848.     cmp    ax,bx            ;have we wrapped around?
  849.     stc
  850.     je    succ_buffer_3        ;yes - exit.
  851.     mov    ds,dx            ;get the new segment to try.
  852.     mov    dx,topbot        ;is the next buffer empty?
  853.     sub    dx,toptop
  854.     add    dx,botbot
  855.     sub    dx,bottop
  856.     mov    dx,next_buffer
  857.     je    succ_buffer_0        ;yes - keep looking.
  858.     mov    bx,ax            ;return the new buffer number.
  859.     clc
  860. succ_buffer_3:
  861.     mov    dx,ds            ;remember the segment.
  862.     assume    ds:data
  863.     pop    ds
  864.     ret
  865.  
  866.  
  867. ;the private routines start here.
  868.  
  869.  
  870. open_up_buffers:
  871. ;enter with dx=paragraph of buffer to open up.
  872.     mov    bx,last_para        ;search for the last buffer in memory.
  873.     mov    cx,bx            ;new next buffer is the same as the old.
  874.     assume    ds:bufseg
  875.  
  876. ;  search for the buffer whose next_buffer is in bx.  We start with
  877. ;  bx=last_para, then we work our way down through the buffers.
  878. ;  We use cx to hold the new next buffer for the buffer we're looking for.
  879.  
  880. open_up_buffers_1:
  881.     mov    ax,first_buffer
  882. open_up_buffers_2:
  883.     mov    ds,ax            ;switch to this buffer.
  884.     mov    ax,next_buffer        ;is this the last one?
  885.     cmp    ax,bx
  886.     jne    open_up_buffers_2    ;no - keep looking.
  887.  
  888. ;  now that we have the buffer we're looking for, tell it where the next
  889. ;  buffer is in memory.
  890.  
  891.     mov    next_buffer,cx
  892.     mov    bx,ds            ;get the current buffer.
  893.     cmp    bx,dx            ;is it the one we want?
  894.     je    open_up_buffers_3    ;yes - go open it.
  895. ;ds, bx now has the buffer to move up in memory.
  896.     mov    ax,cx            ;get the destination paragraph.
  897.     call    move_buffer_higher    ;move the buffer up.
  898.     mov    cx,ax            ;save the new destination paragraph.
  899.     mov    bx,ds            ;save the "next" buffer.
  900.     jmp    open_up_buffers_1
  901. open_up_buffers_3:
  902.     call    open_buffer        ;open the buffer in ds.
  903.     push    es
  904.     pop    ds
  905.     assume    ds:data
  906.     ret
  907.  
  908.  
  909. compact_buffers:
  910. ;move all the buffers as low in memory as they'll go.
  911. ;exit with dx=paragraph of last buffer in memory.
  912.     push    ds
  913.  
  914.     mov    ds,first_buffer
  915.     assume    ds:bufseg
  916. compact_buffers_4:
  917.     mov    bx,next_buffer
  918.     cmp    bx,last_para        ;was this the last buffer in memory?
  919.     je    compact_buffers_3    ;yes - we're done.
  920.  
  921.     call    move_buffer_lower    ;  ax=paragraph to move it to.
  922.     jmp    compact_buffers_4
  923. compact_buffers_3:
  924.     mov    dx,ds            ;return the para of the last buffer.
  925.     pop    ds
  926.     assume    ds:data
  927.     ret
  928.  
  929.  
  930.  
  931. close_up_buffers:
  932. ;close the buffer that is open.
  933. ;exit with dx=paragraph of last buffer in memory.
  934.     push    ds
  935.  
  936.     mov    ds,first_buffer
  937.     assume    ds:bufseg
  938. close_up_buffers_1:
  939.     mov    bx,next_buffer
  940.     cmp    bx,last_para        ;was this the last buffer in memory?
  941.     je    close_up_buffers_2    ;yes - we're done.
  942.  
  943.     call    close_buffer        ;close this buffer.
  944.  
  945.     call    move_buffer_lower    ;  ax=paragraph to move it to.
  946.  
  947.     jmp    close_up_buffers_1
  948.  
  949. close_up_buffers_2:
  950.     mov    dx,ds            ;return the para of the last buffer.
  951.     pop    ds
  952.     assume    ds:data
  953.     ret
  954.  
  955.  
  956.     public    find_buffer
  957. find_buffer:
  958. ;enter with cx=buffer number.
  959. ;exit with nc, dx set to that buffer if it exists, cy otherwise.
  960.     mov    dx,first_buffer
  961.     assume    ds:bufseg
  962. find_buffer_1:
  963.     cmp    dx,last_para        ;at the end?
  964.     je    find_buffer_2
  965.     mov    ds,dx
  966.     mov    dx,next_buffer
  967.     loop    find_buffer_1
  968.     mov    dx,ds            ;get the current buffer back.
  969.     push    es
  970.     pop    ds
  971.     clc
  972.     ret
  973. find_buffer_2:
  974.     push    es            ;restore the data segment.
  975.     pop    ds
  976.     assume    ds:data
  977.     stc
  978.     ret
  979.  
  980.  
  981. code    ends
  982.  
  983.  
  984. code    segment    byte public
  985. ;all the code in this segment is entered with ds=bufseg, es=data
  986.     assume    cs:code, ds:bufseg, es:data
  987.  
  988. ;the following externs are in 'memory'
  989.     extrn    init_vars$: near
  990.     extrn    insert_string$: near
  991.  
  992. ;the following externs are in 'marks'
  993.     extrn    init_marks: near
  994.  
  995. ;the following externs are in 'redisp'
  996.     extrn    adjust_buffers: near
  997.  
  998.  
  999. select_buffer:
  1000. ;enter with ds=buffer to select.
  1001.     mov    textseg,ds        ;save the new current buffer.
  1002.     mov    ax,botbot
  1003.     sub    ax,toptop
  1004.     mov    dx,0
  1005.     mov    cx,100
  1006.     div    cx
  1007.     mov    memsize,ax
  1008.     ret
  1009.  
  1010.  
  1011. init_buffer:
  1012. ;initialize and opens the buffer whose segment is in dx.
  1013. ;exit with cy if there's not enough memory for a new buffer.
  1014.     mov    ax,(offset bufseg_size)+0fh    ;get size of bufseg rounded up.
  1015.     shr    ax,1
  1016.     shr    ax,1
  1017.     shr    ax,1
  1018.     shr    ax,1
  1019.     add    ax,dx
  1020.     cmp    ax,last_para        ;is there enough memory for new buffer?
  1021.     jae    init_buffer_1        ;no.
  1022.     push    ds
  1023.     mov    ds,dx
  1024.     mov    bx,last_para
  1025.     mov    next_buffer,bx
  1026.     push    dx
  1027.     call    init_vars$        ;init most everything
  1028.     call    init_marks        ;init the rest.
  1029.     call    open_buffer        ;open this one up.
  1030.     pop    dx
  1031.     pop    ds
  1032.     clc
  1033.     ret
  1034. init_buffer_1:
  1035.     stc
  1036.     ret
  1037.  
  1038.  
  1039. close_buffer:
  1040. ;close the buffer in ds.
  1041.     mov    ax,topbot        ;if topbot<>bottop, then it's open.
  1042.     cmp    ax,bottop
  1043.     je    close_buffer_1        ;if it's already closed, we're done.
  1044.     mov    di,topbot
  1045.   if 1
  1046.     mov    ax,ds            ;if it's open and it's not the first
  1047.     cmp    ax,first_buffer        ;  buffer, close it.
  1048.     jne    close_buffer_2
  1049.     add    di,2000            ;leave at most this many free bytes 
  1050.     cmp    di,bottop        ;  in buffer one.
  1051.     jae    close_buffer_1        ;but if we can't leave that many, don't bother
  1052. close_buffer_2:
  1053.   endif
  1054.     mov    si,bottop
  1055.     mov    bottop,di        ;save the new bottop.
  1056.     mov    cx,botbot
  1057.     sub    cx,si            ;same as sub cx,bottop
  1058.     add    cx,2            ;include the trailing newline.
  1059.     push    es
  1060.     push    ds
  1061.     pop    es
  1062.     rep    movsb
  1063.     pop    es
  1064.     sub    di,2            ;don't include the newline in botbot.
  1065.     mov    botbot,di
  1066. close_buffer_1:
  1067.     ret
  1068.  
  1069.  
  1070. open_buffer:
  1071. ;select the buffer in ds.
  1072. ;enter with ds=buffer to open.
  1073.     mov    bx,next_buffer
  1074.     mov    ax,ds
  1075.     sub    bx,ax            ;compute the available paragraphs
  1076.     cmp    bx,1000h        ;don't use more than 1000h of them.
  1077.     jb    open_buffer_1
  1078.     mov    bx,1000h
  1079. open_buffer_1:
  1080.     mov    di,bx
  1081.     add    bx,ax            ;make bx=first unused para.
  1082.     mov    cl,04            ;change di from paras into bytes
  1083.     shl    di,cl
  1084.     push    es
  1085.     push    ds
  1086.     pop    es
  1087.     mov    si,botbot
  1088.     std
  1089.     mov    cx,botbot
  1090.     sub    cx,bottop
  1091.     inc    si
  1092.     dec    di
  1093.     movsb                ;move the LF
  1094.     mov    botbot,di
  1095.     movsb                ;move the CR
  1096.     rep    movsb
  1097.     inc    di
  1098.     mov    bottop,di
  1099.     cld
  1100.     pop    es
  1101.     call    select_buffer
  1102.     ret
  1103.  
  1104.  
  1105. move_buffer_higher:
  1106. ;move a buffer higher in memory, closing open buffers.
  1107. ;enter with ds,bx=buffer to move, ax=first unavailable paragraph
  1108. ;exit with ax=paragraph that buffer was moved to (new first unavailable para).
  1109. ;don't destroy dx.
  1110.     push    ax
  1111.     call    close_buffer
  1112.     pop    ax
  1113.  
  1114.     push    es
  1115.     call    buffer_paragraphs
  1116.     sub    ax,cx            ;find new destination para
  1117.     call    adjust_buffers
  1118.     mov    es,ax
  1119.     assume    es:bufseg
  1120.     mov    si,0
  1121.     mov    di,si
  1122.  
  1123.     mov    cx,botbot        ;get the last location used in a buffer,
  1124.     add    cx,2-1            ;  add two for newline and compensate for moving backwards.
  1125.     add    si,cx
  1126.     add    di,cx
  1127. ;    inc    cx            ;don't do this and the following dec
  1128. ;                    ;  because it wastes time.
  1129.     std
  1130.     movsb                ;always move one byte.
  1131. ;    dec    cx            ;this takes care of moving 65536 bytes.
  1132.     rep    movsb
  1133.     cld
  1134.     pop    es
  1135.     assume    es:data
  1136.     ret
  1137.  
  1138.  
  1139. move_buffer_lower:
  1140. ;move a buffer lower in memory.
  1141. ;enter with ds=buffer before the one to be lowered.
  1142. ;exit with ds=new location of lowered buffer.
  1143.     call    buffer_paragraphs
  1144.  
  1145.     mov    ax,ds            ;get the base of this buffer.
  1146.     add    ax,cx            ;get the end of this buffer.
  1147.     mov    bx,next_buffer
  1148.     mov    next_buffer,ax        ;save the pointer to the next buffer.
  1149.     mov    ds,bx            ;get paragraph of buffer to move.
  1150.  
  1151.     cmp    ax,bx            ;leave if the buffer is already there.
  1152.     je    move_buffer_lower_1
  1153.     call    adjust_buffers
  1154.     push    es
  1155.     mov    es,ax
  1156.     mov    si,0
  1157.     mov    di,si
  1158.     mov    cx,botbot        ;get the last location used in a buffer.
  1159.     add    cx,2            ;round up to next paragraph, add two for newline.
  1160.     movsb                ;always move one byte.
  1161.     dec    cx            ;this takes care of moving 65536 bytes.
  1162.     rep    movsb
  1163.     pop    es
  1164.     mov    ds,ax            ;get new para of just moved buffer.
  1165. move_buffer_lower_1:
  1166.     ret
  1167.  
  1168.  
  1169. buffer_paragraphs:
  1170. ;compute the number of paragraphs used by a buffer.
  1171. ;enter with ds=buffer
  1172. ;exit with cx=number of paragraphs.
  1173.     mov    cx,botbot
  1174.     add    cx,0fh+2        ;round up to next paragraph plus two for newline
  1175.     rcr    cx,1            ;ensure that 65536 bytes becomes  
  1176.     shr    cx,1            ; 1000h paragraphs.
  1177.     shr    cx,1
  1178.     shr    cx,1
  1179.     ret
  1180.  
  1181.  
  1182. code    ends
  1183.  
  1184.     end
  1185.